﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using PriceLibrary.PriceService;

using VDataAdapter.EventManagement;
using VDataAdapter.TestPreparation;
using VDataAdapter.Communication;

namespace VDataAdapter
{
    [TestClass()]
    public class TestDataUtilsTest
    {
        [TestMethod()]
        public void TestFirstTimeLoader()
        {
            try
            {
                TestDataUtils.LoadFirstTimeEvent();
            }
            catch (Exception ex)
            {
                Assert.Fail("Loading First Time Event Failed: " + ex.Message);
            }
        }

        [TestMethod()]
        public void TestTransactionsLoader()
        {
            try
            {
                TestDataUtils.LoadTransactionEventList();
            }
            catch (Exception ex)
            {
                Assert.Fail("Loading Transactions Event Failed: " + ex.Message);
            }
        }

        [TestMethod()]
        public void TestStockStop3PricesLoader()
        {
            try
            {
                TestDataUtils.LoadStockTop3PricesEventList();
            }
            catch (Exception ex)
            {
                Assert.Fail("Loading StockStop3Prices Event Failed: " + ex.Message);
            }
        }

        [TestMethod()]
        public void TestLoadMessageOutput()
        {
            string filepath = TestDataFolder.GetTestDataFolder() + "\\messageoutput.test";

            Queue<string> result = TestDataUtils.LoadMessageOutput(filepath);

            // if not exception then the test success
            Assert.IsTrue(result.Count > 0, "Error loading or there is no data message");
           
        }

        [TestMethod()]
        public void TestWriteMessageOutput()
        {
            string path = "message.log";
            string[] test = new string[] { "test1", "test2" };
            TestDataUtils.WriteMessageOutput(test, path);
           
            // check file exists
            Assert.IsTrue(System.IO.File.Exists(path), "File did not written");

            // there must be some data inside
            FileInfo info = new FileInfo(path);
            Assert.IsTrue(info.Length > 0, "File is size is empty: " + info.Length.ToString());

            // clean up
            System.IO.File.Delete(path);
        }

        [TestMethod()]
        public void LoadPutThroughEventFileMustBeInValidFormat()
        {
            try
            {
                string TEST_FOLDER = TestDataFolder.GetTestDataFolder();
                var test = TestDataUtils.LoadPutThroughEvent(TEST_FOLDER + "\\EventMorning" + "\\PutThroughTopPrices.test");
            }
            catch (Exception ex)
            {
                Assert.Fail("Exception while loading PutThrough Top Price file", ex);
            }
        }
    }

    class TestDataUtils
    {
        public static Object[] LoadObjectFromFile(string path)
        {
            Object[] list = PriceServerObjectFile.LoadFromFile(path);

            if (list.Length == 0)
                throw new FormatException("File does not contain any object");

            return list;
        }

        /// <summary>
        /// Load message stock base from file to builda datasource
        /// </summary>
        /// <param name="messageStockTestPath"></param>
        /// <returns></returns>
        public static Dictionary<string, PriceLibrary.PriceService.StockBase> BuildStockBaseFromFile(string messageStockTestPath)
        {
            MessageBuilderRecorder recorder = new MessageBuilderRecorder();

            Dictionary<string, PriceLibrary.PriceService.StockBase> list = recorder.ReadDataSourceStockBase(messageStockTestPath);

            return list;
        }

        #region FirstTime Event
        /// <summary>
        /// Go directly to file
        /// </summary>
        /// <returns></returns>
        public static FirstTimeEvent LoadFirstTimeEvent()
        {
            string TEST_DATA = TestDataFolder.GetTestDataFolder();

            return LoadFirstTimeEvent(TEST_DATA + "\\EventMorning" + "\\GetFirstTime.test");
        }

        public static FirstTimeEvent LoadFirstTimeEvent(string path)
        {
            Tuple<FirstTimeEvent, Queue<string>> t = LoadFirstTimeEventTuple(path);
            return t.Item1;
        }

        public static Tuple<FirstTimeEvent, Queue<String>> LoadFirstTimeEventTuple(string path)
        {            
            PriceServerObjectFile reader = new PriceServerObjectFile();
            Object[] list = reader.ReadFromFile(path);

            //TODO: Clean this, at least add type check
            // this is stupid, but debug and read content of the list to know why
            // too sleepy to do it clean now
            FirstTimeEvent e = (FirstTimeEvent)list[2];
            Queue<String> expected = (Queue<string>)list[3];
            return Tuple.Create(e, expected);
        }
        #endregion
        
        #region Stock Top3Price Loader
        public static Top3PricesEvent[] LoadStockTop3PricesEventList()
        {
            string TEST_DATA = TestDataFolder.GetTestDataFolder();

            return LoadStockStop3PricesEventList(TEST_DATA + "\\EventMorning" + "\\Top3PricesStockChanged.test");
        }

        public static Top3PricesEvent[] LoadStockStop3PricesEventList(string path)
        {
            Tuple<Top3PricesEvent, Queue<string>>[] t = LoadStockTop3PricesEvent(path);
            
            return TestUtils.ToQueueItem1<Top3PricesEvent, Queue<string>>(t)
                .ToArray();
        }
        
        public static Tuple<Top3PricesEvent, Queue<string>>[] LoadStockTop3PricesEvent(string path)
        {
            var result = new List<Tuple<Top3PricesEvent, Queue<string>>>();

            Object[] list = LoadObjectFromFile(path);

            if (IsValidTop3PricesFileFormat(list))
            {
                for (int i = 0; i < list.Length - 1; i = i + 2)
                {
                    var e = (Top3PricesEvent)list[i];
                    var q = (Queue<string>)list[i + 1];
                    result.Add(new Tuple<Top3PricesEvent, Queue<string>>(e, q));
                }
            }

            return result.ToArray();
        }

        private static bool IsValidTop3PricesFileFormat(object[] list)
        {
            try
            {
                for (int i = 0; i < list.Length - 1; i = i + 2)
                {
                    if (list[i].GetType() != typeof(Top3PricesEvent) && list[i + 1].GetType() != typeof(Queue<string>))
                        return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                Assert.Fail("Exception while reading file: " + ex.Message, ex);
            }

            return false;
        }



        #endregion

        #region PutThrough Loader
        public static Tuple<PtTopPricesChangedEvent, Queue<string>>[] LoadPutThroughEvent(string path)
        {
            var result = new List<Tuple<PtTopPricesChangedEvent, Queue<string>>>();
            
            Object[] list = LoadObjectFromFile(path);

            if (IsValidPutThroughFileFormat(list))
            {
                for (int i = 0; i < list.Length - 1; i = i + 2)
                {
                    var e = (PtTopPricesChangedEvent)list[i];
                    var q = (Queue<string>)list[i + 1];                    
                    result.Add(new Tuple<PtTopPricesChangedEvent, Queue<string>>(e, q));
                }
            }

            return result.ToArray();
        }

        private static bool IsValidPutThroughFileFormat(Object[] list)
        {
            try
            {
                for (int i = 0; i < list.Length - 1; i = i + 2)
                {
                    if (list[i].GetType() != typeof(PtTopPricesChangedEvent) && list[i + 1].GetType() != typeof(Queue<string>))
                        return false;
                }
            }
            catch (Exception ex)
            {
                Assert.Fail("Exception while reading file: " + ex.Message, ex);
            }

            return false;
        }

        #endregion

        #region TransacionsData Loader

        /// <summary>
        /// Go directly to file
        /// </summary>
        /// <returns></returns>
        public static TransactionsEvent[] LoadTransactionEventList()
        {
            string TEST_DATA = TestDataFolder.GetTestDataFolder();

            return TestDataUtils.LoadTransactionEventList(TEST_DATA + "\\EventMorning" + "\\Transactions.test");
        }

        public static TransactionsEvent[] LoadTransactionEventList(string filepath)
        {
            Tuple<TransactionsEvent[], Queue<string>> t = LoadTransactionEvent(filepath);
            return t.Item1;
        }

        public static Tuple<TransactionsEvent[], Queue<string>> LoadTransactionEvent(string filepath)
        {
            Object[] list = PriceServerObjectFile.LoadFromFile(filepath);
            var transactionList = new List<TransactionsEvent>();
            var strMsgs = new Queue<string>();

            if (list.Length == 0)
                throw new FormatException("PSATCapturerDataReader: File does not contain any object");

            if (!IsValidTransactionFileFormat(list))
                throw new FormatException("PSATCapturerDataReader: File format is incorrect for Transactions event data");

            foreach (Object obj in list)
            {
                if (obj.GetType().Equals(typeof(TransactionsEvent)))
                {
                    transactionList.Add((TransactionsEvent)obj);
                }

                if (obj.GetType().Equals(typeof(Queue<string>)))
                {
                    strMsgs = (Queue<string>)obj;
                }
            }

            var data = new Tuple<TransactionsEvent[], Queue<string>>(transactionList.ToArray(), strMsgs);
                
            return data;
        }

        private static bool IsValidTransactionFileFormat(Object[] objs)
        {
            if (!HasQueueStringAsLastElement(objs)) return false;

            // all previous element must be TransactionEvent
            for (int i = 0; i < objs.Length - 1; i++)
            {
                if (objs[i].GetType() != typeof(TransactionsEvent))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// This method can be used for all file format
        /// since all event are recorded with their Queue<string> in the last element
        /// </summary>
        /// <param name="objs"></param>
        /// <returns></returns>
        private static bool HasQueueStringAsLastElement(Object[] objs)
        {
            if (objs[objs.Length - 1].GetType() != typeof(Queue<string>))
                return false;
            return true;
        }
        #endregion

        #region MarketInfo Event Data Loader

        public static MarketInfosEvent[] LoadMarketInfoEvent()
        {   
            string TEST_DATA = TestDataFolder.GetTestDataFolder();
            
            Tuple<MarketInfosEvent, Queue<string>>[] data = LoadMarketInfoEvent(TEST_DATA + "\\EventMorning" + "\\ProcessMarketInfo.test");
            Queue<MarketInfosEvent> queue = TestUtils.ToQueueItem1<MarketInfosEvent, Queue<string>>(data);
            return queue.ToArray();
        }

        public static Tuple<MarketInfosEvent, Queue<string>>[] LoadMarketInfoEvent(string path)
        {
            PriceServerObjectFile reader = new PriceServerObjectFile();
            Object[] list = reader.ReadFromFile(path);

            // check if list has even number of items
            // and it has to be alternated between MarketInfoEvent & Queue<string>
            if (!IsValidMarketInfoFileFormat(list))
            {
                throw new Exception("MarketInfo File Format is InValid");
            }

            var result = new List<Tuple<MarketInfosEvent, Queue<string>>>();

            for (int i = 0; i < list.Length - 1; i = i + 2)
            {
                result.Add(Tuple.Create((MarketInfosEvent)list[i], (Queue<String>)list[i + 1]));
            }

            return result.ToArray();
        }

        /// <summary>
        /// Valid file format is alternate between MarketInfoEvent & Queue<string>
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private static bool IsValidMarketInfoFileFormat(Object[] list)
        {
            // it's always even
            if (list.Length % 2 == 1)
            {
                return false;
            }

            for (int i = 0; i < list.Length - 1; i = i + 2)
            {
                if (list[i].GetType() != typeof(MarketInfosEvent) && list[i + 1].GetType() != typeof(Queue<string>))
                    return false;
            }
            return true;
        }
        #endregion

        #region Message Loader

        /// <summary>
        /// Load message output from file
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Queue<string> LoadMessageOutput(string path)
        {
            PriceServerObjectFile reader = new PriceServerObjectFile();
            Object[] list = reader.ReadFromFile(path);
            
            string[] result = TestUtils.CastListObject<string, Object>(list);
            
            return TestUtils.ToQueue<string>(result);
        }

        /// <summary>
        /// Write queue string to file
        /// </summary>
        /// <param name="input"></param>
        /// <param name="path"></param>
        public static void WriteMessageOutput(IEnumerable<string> input, string path)
        {
            StreamWriter writer = new StreamWriter(path);

            foreach (string s in input)
            {
                writer.WriteLine(s);
            }

            writer.Close();
        }
        #endregion
    }

    class TestBuilderMessageUtils
    {
        public static List<MessageMarketTest> GetMessageMarketList()
        {
            string TESTDATA_FOLDER = TestDataFolder.GetTestDataFolder();
            MessageBuilderRecorder recorder = new MessageBuilderRecorder();
            return recorder.GetMessageMarketList(TESTDATA_FOLDER + "\\" +
            recorder.BUILD_MESSAGE_MARKET_FILENAME);
        }


    
    }
}
